Skip to content

[feat]: Scope vueNodesMap per LogicFlow instance to support nested scenarios#2378

Open
EralChen wants to merge 2 commits intodidi:masterfrom
EralChen:feat/vue_nodes_scoped
Open

[feat]: Scope vueNodesMap per LogicFlow instance to support nested scenarios#2378
EralChen wants to merge 2 commits intodidi:masterfrom
EralChen:feat/vue_nodes_scoped

Conversation

@EralChen
Copy link
Contributor

Store Vue node registrations in a WeakMap<GraphModel, ...> and add getVueNodeConfig(type, graphModel) so node configs are resolved per LogicFlow instance instead of from a shared global map.

Updated VueNodeView to read configs through the new scoped getter, which prevents cross-instance registration collisions in multi-instance usage. Kept populating the legacy vueNodesMap (marked deprecated) for backward compatibility.fix(vue-node-registry): isolate node config per LF instance

Store Vue node registrations in a WeakMap<GraphModel, ...> and add getVueNodeConfig(type, graphModel) so node configs are resolved per LogicFlow instance instead of from a shared global map.

Updated VueNodeView to read configs through the new scoped getter, which prevents cross-instance registration collisions in multi-instance usage. Kept populating the legacy vueNodesMap (marked deprecated) for backward compatibility.

@changeset-bot
Copy link

changeset-bot bot commented Feb 28, 2026

⚠️ No Changeset found

Latest commit: 8ecf9ca

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@EralChen
Copy link
Contributor Author

About #2377

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR scopes Vue node registrations to a specific LogicFlow instance to avoid cross-instance collisions (notably in nested / multi-instance scenarios), by introducing a WeakMap<GraphModel, ...> registry and updating VueNodeView to resolve configs through a scoped getter.

Changes:

  • Added per-GraphModel Vue node registry stored in a module-level WeakMap plus getVueNodeConfig(type, graphModel).
  • Deprecated (but still populated) the legacy global vueNodesMap for backward compatibility.
  • Updated VueNodeView to load the node component via getVueNodeConfig(...) instead of the global map.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

File Description
packages/vue-node-registry/src/view.ts Switches runtime config lookup to the new per-instance getter.
packages/vue-node-registry/src/registry.ts Introduces WeakMap-scoped registry, adds getter, keeps legacy global map populated.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +54 to +59
let map = vueNodesMaps.get(lf.graphModel)
if (!map) {
map = {}
vueNodesMaps.set(lf.graphModel, map)
}
map[type] = entry
Copy link

Copilot AI Mar 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both the per-instance registry (map) and the deprecated global vueNodesMap use plain objects indexed by type. If type is not strictly controlled, keys like __proto__/constructor can lead to prototype pollution. Prefer Map<string, VueNodeEntry> (and store it in the WeakMap), or initialize with Object.create(null) and reject dangerous keys.

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot open a new pull request to apply changes based on this feedback

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot open a new pull request to apply changes based on this feedback

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That’s strange
it doesn’t seem to be running. Could you please manually add a commit?

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +91 to +92
if (!nodeConfig) return
const { component } = nodeConfig
Copy link

Copilot AI Mar 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

renderVueComponent() now returns early when getVueNodeConfig() returns undefined. In Teleport mode (isActive()), that can leave a previously-connected teleport entry (items[this.targetId()]) still mounted because disconnect() is only called from unmount(), not here. It can also cause setHtml() to still call startResizeObserver() even though no component was rendered, leaving unnecessary observers attached. Consider cleaning up on this branch (e.g., disconnect/clear any existing mount for this targetId, and avoid starting the resize observer when there is no node config) to prevent stale UI and leaks when a node type isn’t registered for the instance.

Suggested change
if (!nodeConfig) return
const { component } = nodeConfig
if (!nodeConfig) {
// No registered config for this node type; ensure any existing Teleport mount is cleaned up
if (isVue3 && isActive()) {
disconnect(this.targetId())
}
return
}
const { component } = nodeConfig
if (!component) {
// Config exists but has no component; also clean up any existing Teleport mount
if (isVue3 && isActive()) {
disconnect(this.targetId())
}
return
}

Copilot uses AI. Check for mistakes.
Comment on lines +35 to +36
graphModel: GraphModel,
): VueNodeEntry | undefined {
Copy link

Copilot AI Mar 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getVueNodeConfig() calls vueNodesMaps.get(graphModel) directly. Since this function is exported as a public API, it’s easy for JS callers to accidentally pass undefined/null (or a non-object), which will throw a TypeError from WeakMap.get. Consider adding a small runtime guard (or making graphModel optional and returning undefined when it’s missing) so the API fails safely.

Suggested change
graphModel: GraphModel,
): VueNodeEntry | undefined {
graphModel?: GraphModel | null,
): VueNodeEntry | undefined {
if (typeof graphModel !== 'object' || graphModel === null) {
return undefined
}

Copilot uses AI. Check for mistakes.
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants